package worker

import (
	"GoCrontab/crontab/common"
	"context"
	"fmt"
	"github.com/coreos/etcd/clientv3"
	"net"
	"time"
)

type Register struct {
	client *clientv3.Client
	kv     clientv3.KV
	lease  clientv3.Lease

	localIP string // 本机IP
}

//注册ip并且保持续租
func (register Register) keepOnline() {

	var (
		workerRegisterKey      string
		err                    error
		leaseGrantResp         *clientv3.LeaseGrantResponse
		keepAliveChan          <-chan *clientv3.LeaseKeepAliveResponse
		leaseKeepAliveResponse *clientv3.LeaseKeepAliveResponse
		cancelCtx              context.Context
		cancelFunc             context.CancelFunc
	)
	for {
		workerRegisterKey = common.JOB_WORKER_DIR + register.localIP

		//创建租约
		if leaseGrantResp, err = register.lease.Grant(context.TODO(), GlobalConfig.WorkerRegisterLeaseTTL); err != nil {
			goto RETRY
		}

		// 自动续租,接收为一个chan
		if keepAliveChan, err = register.lease.KeepAlive(context.TODO(), leaseGrantResp.ID); err != nil {
			goto RETRY
		}
		cancelCtx, cancelFunc = context.WithCancel(context.TODO())

		//注册到etcd,带leaseId参数&cancelCtx，忽略putResponse的值
		if _, err = register.kv.Put(cancelCtx, workerRegisterKey, "", clientv3.WithLease(leaseGrantResp.ID)); err != nil {
			goto RETRY
		}
		fmt.Println("worker注册ing,key 为： ", workerRegisterKey)
		//处理续租应答
		for {
			select {
			case leaseKeepAliveResponse = <-keepAliveChan:
				if leaseKeepAliveResponse == nil {
					goto RETRY
				}
			}
		}
	}

RETRY:
	time.Sleep(1 * time.Second)
	fmt.Println("worker重试注册中")
	//这没理解，!=nil是代表什么，withCancel什么时候返回nil?。
	//如果失败了需要取消自动续租，（比如put或者续租失败），取消自动续租
	if cancelFunc != nil {
		cancelFunc()
	}

}

var GlobalRegister *Register

//初始化worker的注册服务
func InitRegister() (err error) {
	var (
		config  clientv3.Config
		client  *clientv3.Client
		kv      clientv3.KV
		lease   clientv3.Lease
		localIp string
	)
	config = clientv3.Config{
		Endpoints: GlobalConfig.EtcdEndPoints,
		Username:  GlobalConfig.EtcdUserName,
		Password:  GlobalConfig.EtcdPassword,

		//Millisecond 毫秒,1毫秒等于千分之一秒
		// MicroSecond 微秒,1微秒等于百万分之一秒
		DialTimeout: time.Duration(GlobalConfig.EtcdDialTimeout) * time.Millisecond,
	}

	fmt.Println("%v", config)

	if client, err = clientv3.New(config); err != nil {
		return
	}
	//得到KV和Lease的api子集
	kv = clientv3.NewKV(client)
	lease = clientv3.NewLease(client)

	if localIp, err = getLocalIp(); err != nil {
		return
	}
	GlobalRegister = &Register{
		client:  client,
		kv:      kv,
		lease:   lease,
		localIP: localIp,
	}
	//启动，进行服务注册和keepalive

	go GlobalRegister.keepOnline()
	return
}

//获取本机的worker的 ip地址
func getLocalIp() (ipv4 string, err error) {
	var (
		interfaces []net.Interface
		iface      net.Interface
		addrs      []net.Addr
		addr       net.Addr
		//代表ip地址
		ipNet *net.IPNet
		//是否能够转换
		isIpNet bool
	)

	if interfaces, err = net.Interfaces(); err != nil {
		return
	}

	//从所有的网卡中找出ip地址
	for _, iface = range interfaces {

		if iface.Flags&net.FlagUp == 0 {
			continue //挂着的状态
		}

		if iface.Flags&net.FlagLoopback != 0 {
			continue //loopback 地址
		}

		if addrs, err = iface.Addrs(); err != nil {
			return
		}

		for _, addr = range addrs {
			fmt.Printf("%v", addr)
			if ipNet, isIpNet = addr.(*net.IPNet); isIpNet && !ipNet.IP.IsLoopback() {
				if ipNet.IP.To4() != nil {
					ipv4 = ipNet.IP.String()
				} else {
					continue
				}
			}
			//fmt.Printf("%v", addr)
			//不是lo地址
			//fe80::ed9a:ac29:db44:2570/64 默认返回第一个ipv6，
		}
		return
	}
	err = common.ERR_NO_IP_FOUND
	return
}
